package com.vast.admin.api;

import com.google.gson.Gson;
import com.vast.admin.api.wechatpay.WXPay;
import com.vast.admin.api.wechatpay.WXPayConfig;
import com.vast.admin.api.wechatpay.ZfwWXPayConfig;
import com.vast.system.utils.StringUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;

import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

@Component
@Slf4j
public class WechatApi {

    @Value("${wechat.subscription.appId}")
    private String appId;

    @Value("${wechat.subscription.appsecret}")
    private String appsecret;

    @Autowired
    private RestTemplate restTemplate = new RestTemplate();

    //模板内容字体颜色
    private static final String color = "#173177";

    public static final String PWD = "asdfasdfhgwewr234234asdsc234232sdfsdfSDASF";

    private static String accessToken = null;

    private static Long time = null;

    @Value("${spring.profiles:default}")
    private String profile = "default";

    @Value("${wechat.subscription.prevUrl}")
    private String prevUrl;

    @Value("${wechat.pay.notifyUrl}")
    private String notifyUrl;

    @Autowired
    private ZfwWXPayConfig zfwWXPayConfig;

    public WXPayConfig getWXPayConfig() {
        return zfwWXPayConfig;
    }

    /**
     * 获取AccessToken
     * @return
     */
    public String getAccessToken() {
        if ("zfw".equals(profile)) {
            if (accessToken != null && System.currentTimeMillis() - time < 7100 * 1000) {
                return accessToken;
            }
            accessToken = getWechatAcessToken();
            time = System.currentTimeMillis();
            return accessToken;
        } else {
            return getZfwAcessToken().replace("\"", "");
        }
    }

    /**
     * 微信官方接口获取token
     * @return
     */
    private String getWechatAcessToken() {
        String url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=" + appId + "&secret=" + appsecret;
        Map map = restTemplate.getForObject(url, Map.class, new Object());
        if (map.containsKey("access_token")) {
            return map.get("access_token").toString();
        } else {
            return null;
        }
    }

    /**
     * 调用zfw服务端接口获取， 供开发使用
     * @return
     */
    private String getZfwAcessToken() {
        String url = "http://app.zzzczfw.com/api/test/getAcessToken?pwd=" + PWD;
        return restTemplate.getForObject(url, String.class, new Object());
    }

    /**
     * 获取web端acess_token
     * @param code
     * @return
     */
    public String getWebAcessToken(String code) {
        String url = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=" + appId + "&secret=" + appsecret + "&code=" + code + "&grant_type=authorization_code";
        return restTemplate.getForObject(url, String.class, new Object());
    }

    /**
     * 获取微信用户信息
     * @param wxId
     * @return
     */
    public String getWxUser(String wxId) {
        String url = "https://api.weixin.qq.com/cgi-bin/user/info?access_token=" + getAccessToken() + "&openid=" + wxId + "&lang=zh_CN";
        return restTemplate.getForObject(url, String.class, new Object());
    }

    public String getCallBackIp() {
        String url = "https://api.weixin.qq.com/cgi-bin/get_api_domain_ip?access_token=" + getAccessToken();
        Map map = restTemplate.getForObject(url, Map.class, new Object());
        if (map.containsKey("ip_list")) {
            return map.get("ip_list").toString();
        }
        return null;
    }

    /**
     * 消息推送
     * 参数	是否必填	说明
     * touser	是	接收者openid
     * template_id	是	模板ID
     * url	否	模板跳转链接（海外帐号没有跳转能力）
     * miniprogram	否	跳小程序所需数据，不需跳小程序可不用传该数据
     * appid	是	所需跳转到的小程序appid（该小程序appid必须与发模板消息的公众号是绑定关联关系，暂不支持小游戏）
     * pagepath	否	所需跳转到小程序的具体页面路径，支持带参数,（示例index?foo=bar），要求该小程序已发布，暂不支持小游戏
     * data	是	模板数据
     * color	否	模板内容字体颜色，不填默认为黑色
     * @return
     */
    private boolean pushMsg(String toUser, String msgTemplateId, String toUrl, Map<String, String> data) {
        String url = "https://api.weixin.qq.com/cgi-bin/message/template/send?access_token=" + getAccessToken();
        Map<String, Object> param = new HashMap<>();
        param.put("touser", toUser);
        param.put("template_id", msgTemplateId);
        if (StringUtils.isNotEmpty(toUrl)) {
            param.put("url", toUrl);
        }
        if (data != null) {
            Map<String, Map<String, String>> tmp = new HashMap<>();
            for (Map.Entry<String, String> m : data.entrySet()) {
                Map<String, String> t = new HashMap<>();
                t.put("value", m.getValue());
                t.put("color", color);
                tmp.put(m.getKey(), t);
            }
            param.put("data", tmp);
        }
        ResponseEntity<Map> responseEntity = restTemplate.postForEntity(url, param, Map.class);
        log.info("微信消息推送：toUser[{}], msgTemplateId[{}], toUrl[{}], data[{}], result[{}]", toUser, msgTemplateId, toUrl, new Gson().toJson(data), responseEntity.toString());
        Map result = responseEntity.getBody();
        if (result.containsKey("errcode") && "0".equals(result.get("errcode") + "")) {
            return true;
        }
        return false;
    }

    /**
     * 推送支付消息
     * @param toUser 消息接收用户
     * @param amount 支付金额 元
     * @param contractName 合同名称
     * @param contractId 合同id
     * @return
     */
    public boolean pushPayMsg(String toUser, Double amount, String contractName, Long contractId) {
        String tempId = "NNyBOlp_VaZzRnZa77L47G4y3uNgKDQRbu16RroxP1Y";
        Map<String, String> map = new HashMap<>();
        map.put("first", "合同付款提醒");
        map.put("keyword1", amount + "元");
        map.put("keyword2", contractName);
        map.put("remark", "点击详情查看合同订单信息及付费");
        return pushMsg(toUser, tempId, getPushUrl("pay.html", contractId + ""), map);
    }

    /**
     * 推送实名认证消息
     * @param toUser
     * @param name
     * @param phone
     * @return
     */
    public boolean pushRealNameMsg(String toUser, String name, String phone) {
        String tempId = "643u7NLo2m6AOI5CSYtA1EQT82ubrnS1lJBVqU4kmHw";
        Map<String, String> map = new HashMap<>();
        map.put("first", "实名认证提醒");
        map.put("keyword1", name);
        map.put("keyword2", phone);
        map.put("remark", "点击详情进行实名认证");
        return pushMsg(toUser, tempId, getPushUrl("realname.html", ""), map);
    }

    /**
     * 推送合同签署链接
     * @param toUser
     * @param contractName
     * @param url
     * @return
     */
    public boolean pushContractUrl(String toUser, String contractName, String companyName, String url) {
        String tempId = "IsbUHRlJ4bptgi4auHMdkURBhgna5-gmH1S0v0yjaJM";
        Map<String, String> map = new HashMap<>();
        map.put("first", "合同签署提醒");
        map.put("keyword1", contractName);
        map.put("keyword2", companyName);
        map.put("keyword3", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
        map.put("remark", "点击详情进行合同签署");
        return pushMsg(toUser, tempId, url, map);
    }

    /**
     * 推送APP端账号申请消息
     * @param toUser
     * @param name
     * @param phone
     * @param companyName
     * @return
     */
    public boolean pushAppApplyMsg(String toUser, String name, String phone, String companyName) {
        String tempId = "0vPhXXscRQbl86OEVw47GvCNqC3mN_1PC7hhKk6e-gI";
        Map<String, String> map = new HashMap<>();
        map.put("first", "账号申请提醒");
        map.put("keyword1", companyName + "-" + name + "-" + phone);
        map.put("keyword2", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
        map.put("remark", "请尽快前往“客户管理”-“账号申请”菜单中处理");
        return pushMsg(toUser, tempId, null, map);
    }

    /**
     * 获取推送链接
     * @param htmlName
     * @param state
     * @return
     */
    private String getPushUrl(String htmlName, String state) {
        String redirectUrl = prevUrl + htmlName;
        try {
            redirectUrl = URLEncoder.encode(redirectUrl, "utf-8");
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        return "https://open.weixin.qq.com/connect/oauth2/authorize?appid=" + appId
                + "&redirect_uri=" + redirectUrl +"&response_type=code&scope=snsapi_base&state=" + state +"#wechat_redirecta";
    }

    /**
     * 微信支付下单接口
     * @param outTradeNo 订单号
     * @param totalFee 金额 分
     * @param spbillCreateIp ip
     * @param openId 用户wxid
     * @return
     */
    public Map<String, String> unifiedOrder(String outTradeNo, Long totalFee, String spbillCreateIp, String openId) {
        try {
            WXPay wxPay = new WXPay(zfwWXPayConfig);
            Map<String, String> stringStringMap = new HashMap<>();
            stringStringMap.put("body", "合同签署");
            stringStringMap.put("out_trade_no", outTradeNo);
            stringStringMap.put("total_fee", totalFee + "");
            stringStringMap.put("spbill_create_ip", spbillCreateIp);
            stringStringMap.put("notify_url", notifyUrl + "callback");
            stringStringMap.put("trade_type", "JSAPI");
            stringStringMap.put("openid", openId);
            return wxPay.unifiedOrder(stringStringMap);
        } catch (Exception e) {
            log.error("微信支付下单失败", e);
        }
        return null;
    }

    /**
     * 获取参数
     * @return
     */
    public Map<String, String> prePay(String prepayId) {
        try {
            return new WXPay(zfwWXPayConfig).getPayParam(prepayId);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 微信支付退款
     * @param outTradeNo 微信支付订单号
     * @param outRefundNo 退款单号
     * @param amount 金额 单位 分
     * @param refundDesc 退款描述
     * @return
     * 字段名    变量名	必填	类型	示例值	描述
     * 业务结果	result_code	是	String(16)	SUCCESS
     * SUCCESS/FAIL
     *
     * SUCCESS退款申请接收成功，结果通过退款查询接口查询
     *
     * FAIL 提交业务失败
     *
     * 错误代码	err_code	否	String(32)	SYSTEMERROR	列表详见错误码列表
     * 错误代码描述	err_code_des	否	String(128)	系统超时	结果信息描述
     * 公众账号ID	appid	是	String(32)	wx8888888888888888	微信分配的公众账号ID
     * 商户号	mch_id	是	String(32)	1900000109	微信支付分配的商户号
     * 随机字符串	nonce_str	是	String(32)	5K8264ILTKCH16CQ2502SI8ZNMTM67VS	随机字符串，不长于32位
     * 签名	sign	是	String(32)	5K8264ILTKCH16CQ2502SI8ZNMTM67VS	签名，详见签名算法
     * 微信支付订单号	transaction_id	是	String(32)	4007752501201407033233368018	微信订单号
     * 商户订单号	out_trade_no	是	String(32)	33368018	商户系统内部订单号，要求32个字符内，只能是数字、大小写字母_-|*@ ，且在同一个商户号下唯一。
     * 商户退款单号	out_refund_no	是	String(64)	121775250	商户系统内部的退款单号，商户系统内部唯一，只能是数字、大小写字母_-|*@ ，同一退款单号多次请求只退一笔。
     * 微信退款单号	refund_id	是	String(32)	2007752501201407033233368018	微信退款单号
     * 退款金额	refund_fee	是	Int	100	退款总金额,单位为分,可以做部分退款
     * 应结退款金额	settlement_refund_fee	否	Int	100	去掉非充值代金券退款金额后的退款金额，退款金额=申请退款金额-非充值代金券退款金额，退款金额<=申请退款金额
     * 标价金额	total_fee	是	Int	100	订单总金额，单位为分，只能为整数，详见支付金额
     * 应结订单金额	settlement_total_fee	否	Int	100	去掉非充值代金券金额后的订单总金额，应结订单金额=订单金额-非充值代金券金额，应结订单金额<=订单金额。
     * 标价币种	fee_type	否	String(8)	CNY	订单金额货币类型，符合ISO 4217标准的三位字母代码，默认人民币：CNY，其他值列表详见货币类型
     * 现金支付金额	cash_fee	是	Int	100	现金支付金额，单位为分，只能为整数，详见支付金额
     * 现金支付币种	cash_fee_type	否	String(16)	CNY	货币类型，符合ISO 4217标准的三位字母代码，默认人民币：CNY，其他值列表详见货币类型
     * 现金退款金额	cash_refund_fee	否	Int	100	现金退款金额，单位为分，只能为整数，详见支付金额
     * 代金券类型	coupon_type_$n	否	String(8)	CASH
     * CASH--充值代金券
     * NO_CASH---非充值代金券
     *
     * 订单使用代金券时有返回（取值：CASH、NO_CASH）。$n为下标,从0开始编号，举例：coupon_type_0
     *
     * 代金券退款总金额	coupon_refund_fee	否	Int	100	代金券退款金额<=退款金额，退款金额-代金券或立减优惠退款金额为现金，说明详见代金券或立减优惠
     * 单个代金券退款金额	coupon_refund_fee_$n	否	Int	100	代金券退款金额<=退款金额，退款金额-代金券或立减优惠退款金额为现金，说明详见代金券或立减优惠
     * 退款代金券使用数量	coupon_refund_count	否	Int	1	退款代金券使用数量
     * 退款代金券ID	coupon_refund_id_$n	否	String(20)	10000 	退款代金券ID, $n为下标，从0开始编号
     */
    public Map<String, String> refund(String outTradeNo, String outRefundNo, long amount, String refundDesc) {
        try {
            WXPay wxPay = new WXPay(zfwWXPayConfig);
            Map<String, String> stringStringMap = new HashMap<>();
            stringStringMap.put("out_trade_no", outTradeNo);
            stringStringMap.put("out_refund_no", outRefundNo);
            stringStringMap.put("total_fee", String.valueOf(amount));
            stringStringMap.put("refund_fee", String.valueOf(amount));
            stringStringMap.put("refund_desc", refundDesc);
            stringStringMap.put("notify_url", notifyUrl + "refundCallback");
            return wxPay.refund(stringStringMap);
        } catch (Exception e) {
            log.error("微信退款申请失败", e);
        }
        return null;
    }

}
